
#include <cstring>
#include "sdk.h"
#ifndef CB_PRECOMP
#include <wx/event.h>
#include <wx/frame.h>
#include <wx/intl.h>
#include <wx/menu.h>
#include <wx/menuitem.h>
#include <wx/string.h>
#include "sdk_events.h"
#include "manager.h"
#include "configmanager.h"
#include "projectmanager.h"
#include "personalitymanager.h"
#include "cbworkspace.h"
#include "cbproject.h"
#include "logmanager.h"
#include "editorbase.h"
#endif
#include <wx/stdpaths.h>
#include <wx/process.h>

#include <wx/dnd.h>
#include <wx/utils.h>

#include "version.h"
#include "codesnippets.h"
#include "codesnippetswindow.h"
#include "snippetsconfig.h"
#include "GenericMessageBox.h"
#include "cbauibook.h"


namespace {

  PluginRegistrant<CodeSnippets> reg(_T("CodeSnippets"));



  class OpenFilesListData : public wxTreeItemData {
    public:
      OpenFilesListData(EditorBase* ed) : ed(ed) {}
      EditorBase* GetEditor() const {
        return ed;
      }
    private:
      EditorBase* ed;
  };

};
int idViewSnippets = wxNewId();


BEGIN_EVENT_TABLE(CodeSnippets, cbPlugin)
  EVT_UPDATE_UI(idViewSnippets, CodeSnippets::OnUpdateUI)
  EVT_MENU(idViewSnippets, CodeSnippets::OnViewSnippets)
  EVT_ACTIVATE(CodeSnippets::OnActivate)


END_EVENT_TABLE()


CodeSnippets::CodeSnippets() {
  m_bMouseCtrlKeyDown = false;
  m_bMouseLeftKeyDown = false;
  m_bMouseIsDragging = false;
  m_bDragCursorOn = false;
  m_MouseDownX = m_MouseDownY = 0;
  m_MouseUpX = m_MouseUpY = 0;
  #if !wxCHECK_VERSION(2, 8, 12)
  m_prjTreeItemAtKeyUp = m_prjTreeItemAtKeyDown = 0;
  #endif
  m_bMouseExitedWindow = false;
  m_bBeginInternalDrag = false;
  m_pDragCursor = new wxCursor(wxCURSOR_HAND);
}

CodeSnippets::~CodeSnippets() {
}

void CodeSnippets::OnAttach() {
  if(GetConfig()) {
    wxMessageBox(wxT("CodeSnippets will enable on CodeBlocks restart."));
    return;
  }
  SetConfig(new CodeSnippetsConfig);
  GetConfig()->m_bIsPlugin = true;
  GetConfig()->SetOpenFilesList(0);
  m_pAppWin = Manager::Get()->GetAppWindow();
  GetConfig()->AppName = wxT("codesnippets");
  GetConfig()->pMainFrame  = Manager::Get()->GetAppWindow() ;
  GetConfig()->m_pMenuBar = Manager::Get()->GetAppFrame()->GetMenuBar();
  AppVersion pgmVersion;
  #if LOGGING
  wxLog::EnableLogging(true);
  m_pLog = new wxLogWindow(m_pAppWin, _T(" CodeSnippets Plugin"), true, false);
  wxLog::SetActiveTarget(m_pLog);
  m_pLog->Flush();
  m_pLog->GetFrame()->SetSize(20, 30, 600, 300);
  #if defined(LOGGING)
  LOGIT(_T("CodeSnippets Plugin Logging Started[%s]"), pgmVersion.GetVersion().c_str());
  #endif
  #endif
  PluginInfo* pInfo = (PluginInfo*)(Manager::Get()->GetPluginManager()->GetPluginInfo(this));
  pInfo->version = pgmVersion.GetVersion();
  GetConfig()->m_ConfigFolder = Manager::Get()->GetConfigManager(_T("app"))->GetConfigFolder();
  #if defined(LOGGING)
  LOGIT(_T("Argv[0][%s] Cwd[%s]"), wxTheApp->argv[0], ::wxGetCwd().GetData());
  #endif
  GetConfig()->m_ExecuteFolder = FindAppPath(wxTheApp->argv[0], ::wxGetCwd(), wxEmptyString);
  GetConfig()->m_ConfigFolder.Replace(_T("//"), _T("/"));
  GetConfig()->m_ExecuteFolder.Replace(_T("//"), _T("/"));
  #if defined(LOGGING)
  LOGIT(wxT("CfgFolder[%s]"), GetConfig()->m_ConfigFolder.c_str());
  LOGIT(wxT("ExecFolder[%s]"), GetConfig()->m_ExecuteFolder.c_str());
  #endif
  wxString m_Personality = Manager::Get()->GetPersonalityManager()->GetPersonality();
  if(m_Personality == wxT("default"))
    m_Personality = wxEmptyString;
  #if defined(LOGGING)
  LOGIT(_T("Personality is[%s]"), m_Personality.GetData());
  #endif
  wxString m_CfgFilenameStr = GetConfig()->m_ExecuteFolder + wxFILE_SEP_PATH;
  if(not m_Personality.IsEmpty())
    m_CfgFilenameStr << m_Personality + wxT(".") ;
  m_CfgFilenameStr << GetConfig()->AppName + _T(".ini");
  if(::wxFileExists(m_CfgFilenameStr))
    ;
  else {
    m_CfgFilenameStr = GetConfig()->m_ConfigFolder + wxFILE_SEP_PATH;
    if(not m_Personality.IsEmpty())
      m_CfgFilenameStr <<  m_Personality + wxT(".") ;
    m_CfgFilenameStr << GetConfig()->AppName + _T(".ini");
    if(not ::wxDirExists(GetConfig()->m_ConfigFolder))
      ::wxMkdir(GetConfig()->m_ConfigFolder);
  }
  GetConfig()->SettingsSnippetsCfgPath = m_CfgFilenameStr;
  #if defined(LOGGING)
  LOGIT(_T("SettingsSnippetsCfgPath[%s]"), GetConfig()->SettingsSnippetsCfgPath.c_str());
  #endif
  GetConfig()->SettingsCBConfigPath = GetConfig()->m_ConfigFolder;
  wxString cbConfigPath = GetConfig()->m_ExecuteFolder + wxFILE_SEP_PATH + _T("default.conf");
  if(::wxFileExists(cbConfigPath))
    GetConfig()->SettingsCBConfigPath = GetConfig()->m_ExecuteFolder;
  GetConfig()->SettingsLoad();
  m_pProjectMgr = Manager::Get()->GetProjectManager();
  m_pProjectMgr->GetUI().GetNotebook()->SetDropTarget(new DropTargets(this));
  #if defined(__WXMSW__)
  wxTreeCtrl* pPrjTree = m_pProjectMgr->GetUI().GetTree();
  m_oldCursor = pPrjTree->GetCursor();
  SetTreeCtrlHandler(pPrjTree, wxEVT_COMMAND_TREE_BEGIN_DRAG);
  #endif
  GetConfig()->SetOpenFilesList(FindOpenFilesListWindow());
  if(GetConfig()->GetOpenFilesList()) {
    GetConfig()->GetOpenFilesList()->SetDropTarget(new DropTargets(this));
    #if defined(__WXMSW__)
    SetTreeCtrlHandler(GetConfig()->GetOpenFilesList(),  wxEVT_COMMAND_TREE_BEGIN_DRAG);
    #endif
  }
  m_nOnActivateBusy = 0;
  GetConfig()->m_appIsShutdown = false;
  GetConfig()->m_appIsDisabled = false;
  GetConfig()->pSnipImages = new SnipImages();
  if(not GetConfig()->IsExternalWindow())
    CreateSnippetWindow();
  #if defined(LOGGING)
  #endif
  Manager::Get()->RegisterEventSink(cbEVT_SWITCH_VIEW_LAYOUT, new cbEventFunctor<CodeSnippets, CodeBlocksLayoutEvent>(this, &CodeSnippets::OnSwitchViewLayout));
  Manager::Get()->RegisterEventSink(cbEVT_SWITCHED_VIEW_LAYOUT, new cbEventFunctor<CodeSnippets, CodeBlocksLayoutEvent>(this, &CodeSnippets::OnSwitchedViewLayout));
  Manager::Get()->RegisterEventSink(cbEVT_DOCK_WINDOW_VISIBILITY, new cbEventFunctor<CodeSnippets, CodeBlocksDockEvent>(this, &CodeSnippets::OnDockWindowVisability));
  Manager::Get()->RegisterEventSink(cbEVT_APP_STARTUP_DONE, new cbEventFunctor<CodeSnippets, CodeBlocksEvent>(this, &CodeSnippets::OnAppStartupDone));
  Manager::Get()->RegisterEventSink(cbEVT_APP_START_SHUTDOWN, new cbEventFunctor<CodeSnippets, CodeBlocksEvent>(this, &CodeSnippets::OnAppStartShutdown));
}

void CodeSnippets::OnRelease(bool appShutDown) {
  #if defined(LOGGING)
  LOGIT(_T("CodeSnippets OnRelease[%s]"), appShutDown ? wxT("true") : wxT("false"));
  #endif
  if(GetConfig()->m_appIsShutdown)
    return;
  if(not appShutDown) {
    OnDisable(appShutDown);
    return;
  }
  GetConfig()->m_appIsShutdown = true;
}

void CodeSnippets::BuildMenu(wxMenuBar* menuBar) {
  if(GetConfig()->m_appIsShutdown)
    return;
  if(GetConfig()->m_appIsDisabled)
    return;
  GetConfig()->m_pMenuBar = menuBar;
  bool isSet = false;
  int idx = menuBar->FindMenu(_("View"));
  if(idx != wxNOT_FOUND)
    do {
      wxMenu* viewMenu = menuBar->GetMenu(idx);
      wxMenuItemList& items = viewMenu->GetMenuItems();
      for(size_t i = 0; i < items.GetCount(); ++i) {
        if(items[i]->IsSeparator()) {
          viewMenu->InsertCheckItem(i, idViewSnippets, _("Code snippets"), _("Toggle displaying the code snippets."));
          isSet = true;
          break;
        }
      }
      if(not isSet)
        viewMenu->AppendCheckItem(idViewSnippets, _("Code snippets"), _("Toggle displaying the code snippets."));
    } while(0);
  #if defined(LOGGING)
  LOGIT(wxT("Menubar[%p]idViewSnippets[%d]"), menuBar, idViewSnippets);
  #endif
}

void CodeSnippets::OnDisable(bool appShutDown) {
  if(GetConfig()->m_appIsShutdown)
    return;
  if(GetConfig()->m_appIsDisabled)
    return;
  if(appShutDown)
    return;
  GetConfig()->m_appIsDisabled = true;
  wxMenuBar* pbar = GetConfig()->m_pMenuBar;
  pbar->Check(idViewSnippets, false);
  CodeBlocksDockEvent evt(cbEVT_HIDE_DOCK_WINDOW);
  evt.pWindow = GetSnippetsWindow();
  Manager::Get()->ProcessEvent(evt);
  return;
}

void CodeSnippets::OnAppStartupDone(CodeBlocksEvent& event) {
  if(not GetConfig()->GetOpenFilesList()) {
    GetConfig()->SetOpenFilesList(FindOpenFilesListWindow());
    if(GetConfig()->GetOpenFilesList()) {
      GetConfig()->GetOpenFilesList()->SetDropTarget(new DropTargets(this));
      #if defined(__WXMSW__)
      SetTreeCtrlHandler(GetConfig()->GetOpenFilesList(),  wxEVT_COMMAND_TREE_BEGIN_DRAG);
      #endif
      #if defined(LOGGING)
      LOGIT(_T("OpenFilesList found @[%p]"), GetConfig()->GetOpenFilesList());
      #endif
    } else {
      #if defined(LOGGING)
      LOGIT(_T("OpenFilesList *NOT* found."));
      #endif
    }
  }
  event.Skip();
}

void CodeSnippets::OnAppStartShutdown(CodeBlocksEvent& event) {
  if(GetConfig()->m_appIsShutdown)
    return;
  if(not GetSnippetsWindow())
    return;
  while(m_nOnActivateBusy) {
    wxMilliSleep(10) ;
    wxYield();
  }
  #if defined(__WXMSW__)
  wxTreeCtrl* pPrjTree = Manager::Get()->GetProjectManager()->GetUI().GetTree();
  RemoveTreeCtrlHandler(pPrjTree, wxEVT_COMMAND_TREE_BEGIN_DRAG);
  RemoveTreeCtrlHandler(GetConfig()->GetOpenFilesList(),  wxEVT_COMMAND_TREE_BEGIN_DRAG);
  #endif
  GetSnippetsWindow()->GetSnippetsTreeCtrl()->SaveAllOpenEditors();
  if(GetSnippetsWindow())
    if(GetSnippetsWindow()->GetFileChanged())
      GetSnippetsWindow()->GetSnippetsTreeCtrl()->SaveItemsToFile(GetConfig()->SettingsSnippetsXmlPath);
  wxCloseEvent closeevt;
  closeevt.SetEventObject(GetConfig()->GetSnippetsWindow());
  GetConfig()->GetSnippetsWindow()->OnClose(closeevt);
  GetConfig()->SettingsSave();
}

void CodeSnippets::CreateSnippetWindow() {
  SetSnippetsWindow(new CodeSnippetsWindow(GetConfig()->pMainFrame));
  if(GetConfig()->IsApplication()) {
    GetSnippetsWindow()->SetSize(GetConfig()->windowXpos, GetConfig()->windowYpos,
                                 GetConfig()->windowWidth, GetConfig()->windowHeight);
  }
  CodeBlocksDockEvent evt(cbEVT_ADD_DOCK_WINDOW);
  evt.name = _T("CodeSnippetsPane");
  evt.title = _(" CodeSnippets");
  evt.pWindow = GetSnippetsWindow();
  evt.desiredSize.Set(300, 400);
  evt.floatingSize.Set(300, 400);
  evt.minimumSize.Set(30, 40);
  evt.dockSide = CodeBlocksDockEvent::dsFloating;
  evt.stretch = true;
  if(GetConfig()->GetSettingsWindowState().Contains(wxT("Docked"))) {
    evt.dockSide = CodeBlocksDockEvent::dsLeft;
    evt.stretch = true;
  }
  Manager::Get()->ProcessEvent(evt);
}

void CodeSnippets::SetSnippetsWindow(CodeSnippetsWindow* p) {
  GetConfig()->pSnippetsWindow = p;
}

void CodeSnippets::OnViewSnippets(wxCommandEvent& event) {
  wxMenuBar* pbar = Manager::Get()->GetAppFrame()->GetMenuBar();
  wxMenu* pViewMenu = 0;
  wxMenuItem* pViewItem = pbar->FindItem(idViewSnippets, &pViewMenu);
  #if defined(LOGGING)
  LOGIT(_T("OnViewSnippets [%s] Checked[%d] IsShown[%d]"),
        GetConfig()->IsFloatingWindow() ? wxT("float") : wxT("dock"),
        pViewMenu->IsChecked(idViewSnippets),
        IsWindowReallyShown(GetSnippetsWindow())
       );
  #endif
  if(not GetSnippetsWindow())
    CreateSnippetWindow();
  #if defined(LOGGING)
  LOGIT(_T("OnView [%s] Checked[%d] IsShown[%d]"),
        GetConfig()->IsFloatingWindow() ? wxT("float") : wxT("dock"),
        pViewItem->IsChecked(),
        IsWindowReallyShown(GetSnippetsWindow())
       );
  #endif
  if(IsWindowReallyShown(GetSnippetsWindow()))
    do {
      if(not pViewItem->IsChecked()) {
        if(GetConfig()->IsFloatingWindow())
          GetConfig()->SettingsSaveWinPosition();
        #if defined(LOGGING)
        LOGIT(_T("OnViewSnippets Saving Settings on HideWindow"));
        #endif
        GetConfig()->SettingsSave();
      }
    } while(0);
  CodeBlocksDockEvent evt(pViewItem->IsChecked() ? cbEVT_SHOW_DOCK_WINDOW : cbEVT_HIDE_DOCK_WINDOW);
  evt.pWindow = GetSnippetsWindow();
  Manager::Get()->ProcessEvent(evt);
}

void CodeSnippets::OnUpdateUI(wxUpdateUIEvent&) {
  wxMenuBar* pbar = Manager::Get()->GetAppFrame()->GetMenuBar();
  #if defined(LOGGING)
  LOGIT(_T("OnUpdateUI Window[%p],Pid[%d]"), GetSnippetsWindow(), m_ExternalPid);
  #endif
  if(not GetSnippetsWindow()) {
    pbar->Check(idViewSnippets, false);
    #if defined(LOGGING)
    LOGIT(_T("OnUpdateUI Check[%s]"), wxT("to OFF"));
    #endif
    return;
  }
  if(GetSnippetsWindow()) {
    pbar->Check(idViewSnippets, IsWindowReallyShown(GetSnippetsWindow()));
    return;
  }
}

void CodeSnippets::OnIdle(wxIdleEvent& event) {
  if(GetConfig()->m_appIsShutdown)
  { event.Skip(); return; }
  if(m_nOnActivateBusy) {
    event.Skip();
    return;
  }
  if(GetSnippetsWindow() && GetSnippetsWindow()->IsTreeBusy()) {
    event.Skip();
    return;
  }
  if(GetConfig()->m_bWindowStateChanged) {
    if(GetSnippetsWindow() && GetConfig()->m_bWindowStateChanged)
      CloseDockWindow();
    if(not GetSnippetsWindow()) {
      if(GetConfig()->m_bWindowStateChanged) {
        GetConfig()->m_bWindowStateChanged = false;
        {
          CreateSnippetWindow();
          bool bExternalRequest = GetConfig()->GetSettingsWindowState().Contains(wxT("External"));
          if(not bExternalRequest) {
            CodeBlocksDockEvent evt(cbEVT_SHOW_DOCK_WINDOW);
            evt.pWindow = GetSnippetsWindow();
            Manager::Get()->ProcessEvent(evt);
          }
        }
      }
    }
    GetConfig()->m_bWindowStateChanged = false;
  }
  event.Skip();
}

void CodeSnippets::CloseDockWindow() {
  if(not GetSnippetsWindow())
    return;
  bool bOpen = IsWindowReallyShown(GetSnippetsWindow());
  if(bOpen && GetConfig()->IsFloatingWindow()) {
    CodeBlocksDockEvent evt(cbEVT_REMOVE_DOCK_WINDOW);
    evt.pWindow = GetSnippetsWindow();
    Manager::Get()->ProcessEvent(evt);
  }
  if(GetSnippetsWindow()) {
    CodeBlocksDockEvent evt(cbEVT_REMOVE_DOCK_WINDOW);
    evt.pWindow = GetSnippetsWindow();
    Manager::Get()->ProcessEvent(evt);
    GetSnippetsWindow()->Destroy();
    SetSnippetsWindow(0);
    #if defined(LOGGING)
    LOGIT(_T("CloseDockWindow:SnippetsWindow [%s]"), bOpen ? wxT("Open") : wxT("Closed"));
    #endif
  }
}

void CodeSnippets::OnSwitchViewLayout(CodeBlocksLayoutEvent& event) {
  #if defined(LOGGING)
  LOGIT(_T("cbEVT_SWITCH_LAYOUT[%s]"), event.layout.c_str());
  #endif
  event.Skip();
}

void CodeSnippets::OnSwitchedViewLayout(CodeBlocksLayoutEvent& event) {
  #if defined(LOGGING)
  LOGIT(_T("cbEVT_SWITCHED_LAYOUT[%s]"), event.layout.c_str());
  #endif
  event.Skip();
}

void CodeSnippets::OnDockWindowVisability(CodeBlocksDockEvent& event) {
  #if defined(LOGGING)
  LOGIT(_T("cbEVT_DOCK_WINDOW_VISIBILITY[%p]"), event.GetId());
  #endif
  wxMenuBar* pbar = Manager::Get()->GetAppFrame()->GetMenuBar();
  if(not IsWindowReallyShown(GetSnippetsWindow()))
    pbar->Check(idViewSnippets, false);
  event.Skip();
}

void CodeSnippets::OnActivate(wxActivateEvent& event) {
  if(m_nOnActivateBusy) {
    event.Skip();
    return;
  }
  ++m_nOnActivateBusy;
  do {
    if(not event.GetActive())
      break;
    if(not GetConfig()->GetSnippetsWindow())
      break;
    if(not GetConfig()->GetSnippetsTreeCtrl())
      break;
    CodeSnippetsWindow* p = GetConfig()->GetSnippetsWindow();
    p->CheckForExternallyModifiedFiles();
  } while(0);
  m_nOnActivateBusy = 0;
  event.Skip();
  return;
}

bool CodeSnippets::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& files) {
  #ifdef LOGGING
  LOGIT(_T("CodeSnippets::OnDropFiles Entered"));
  #endif
  wxDropTarget*  pMainDrpTgt = GetConfig()->GetMainFrame()->GetDropTarget();
  if(not pMainDrpTgt) {
    #ifdef LOGGING
    LOGIT(_T("CodeSnippets::OnDropFiles Error pMainDrpTgt is %p"), pMainDrpTgt);
    #endif
    return false;
  }
  bool bRC = ((wxMyFileDropTarget*)pMainDrpTgt)->OnDropFiles(x, y, files);
  #ifdef LOGGING
  LOGIT(_T("CodeSnippets::OnDropFiles MainFrame returned %s"), bRC ? wxT("True") : wxT("False"));
  #endif
  return bRC;
}

bool CodeSnippets::GetTreeSelectionData(const wxTreeCtrl* pTree, const wxTreeItemId itemID, wxString& selString) {
  selString = wxEmptyString;
  if(not pTree)
    return false;
  if((pTree == m_pProjectMgr->GetUI().GetTree())
      or (pTree == GetConfig()->GetOpenFilesList()))
    ;
  else
    return false;
  #ifdef LOGGING
  #endif
  wxTreeItemId sel = itemID;
  if(itemID.IsOk())
    sel = itemID;
  else
    return false;
  if(not sel)
    return false;
  #ifdef LOGGING
  LOGIT(_T("Selection:%p"), sel.m_pItem);
  #endif
  if(pTree == GetConfig()->GetOpenFilesList()) {
    EditorBase* ed = static_cast<EditorBase*>(static_cast<OpenFilesListData*>(pTree->GetItemData(sel))->GetEditor());
    selString = (ed ? ed->GetFilename() : wxT(""));
  }
  if(pTree == m_pProjectMgr->GetUI().GetTree()) {
    if(sel && sel == pTree->GetRootItem()) {
      cbWorkspace* pWorkspace = m_pProjectMgr->GetWorkspace();
      if(not pWorkspace)
        return false;
      selString = pWorkspace->GetFilename();
      return (not selString.IsEmpty());
    }
    FileTreeData* ftd = (FileTreeData*)pTree->GetItemData(sel);
    if(not ftd)
      return false;
    if(ftd->GetKind() == FileTreeData::ftdkProject) {
      cbProject* pPrj = ftd->GetProject();
      if(pPrj)
        selString = pPrj->GetFilename();
    }
    if(ftd->GetKind() == FileTreeData::ftdkFile) {
      ProjectFile* pPrjFile = ftd->GetProjectFile();
      if(not pPrjFile)
        return false;
      selString = pPrjFile->file.GetFullPath();
    }
  }
  return (not selString.IsEmpty()) ;
}
wxArrayString* CodeSnippets::TextToFilenames(const wxString& str) {
  wxArrayString* pFilenames = new wxArrayString;
  if((0 == str.Freq('\r')) && (0 == str.Freq('\n')))
    pFilenames->Add(str);
  else {
    wxString ostr;
    for(size_t i = 0; i < str.Length(); i++) {
      if((str[i] != '\r') && (str[i] != '\n'))
        ostr.Append(str[i]);
      else {
        pFilenames->Add(ostr);
        ostr.Empty();
        if(((i + 1) < str.Length()) && (str[i + 1] == '\r'))
          i++;
        if(((i + 1) < str.Length()) && (str[i + 1] == '\n'))
          i++;
      }
    }
    if(not ostr.IsEmpty())
      pFilenames->Add(ostr);
  }
  for(size_t i = 0; i < pFilenames->GetCount(); i++) {
    #ifdef LOGGING
    LOGIT(wxT("CodeSnippets::OnPaste:Text converted to filename %d:[%s]"), i, pFilenames->Item(i).GetData());
    #endif
    if(not wxFileExists(pFilenames->Item(i))) {
      pFilenames->RemoveAt(i);
      i--;
    }
  }
  return pFilenames;
}


DropTargets::DropTargets(CodeSnippets* pcbDndExtn)
  : wxDropTarget() {
  m_pcbDndExtn = pcbDndExtn;
  m_file = new wxFileDataObject();
  m_text = new wxTextDataObject();
  DropTargetsComposite *data = new DropTargetsComposite();
  data->Add((wxDataObjectSimple*)m_file);
  data->Add((wxDataObjectSimple*)m_text, true);
  SetDataObject(data);
  #ifdef LOGGING
  LOGIT(wxT("DropTargets::ctor set"));
  #endif
}


wxDragResult DropTargets::OnData(wxCoord x, wxCoord y, wxDragResult def) {
  #ifdef LOGGING
  LOGIT(wxT("DropTargets::OnData"));
  #endif
  if(!GetData())
    return wxDragNone;
  wxDataObjectSimple *obj = ((DropTargetsComposite *)GetDataObject())->GetLastDataObject();
  if(obj == ((wxDataObjectSimple*)m_file)) {
    if(not OnDataFiles(x, y, m_file->GetFilenames()))
      def = wxDragNone;
  } else if(obj == ((wxDataObjectSimple*)m_text)) {
    if(not OnDataText(x, y, m_text->GetText()))
      def = wxDragNone;
  } else
    def = wxDragNone;
  return def;
}


bool DropTargets::OnDataFiles(wxCoord WXUNUSED(x), wxCoord WXUNUSED(y),
                              const wxArrayString& filenames) {
  #ifdef LOGGING
  LOGIT(wxT("DropTargets::OnDataFiles"));
  #endif
  m_pcbDndExtn->OnDropFiles(1, 1, filenames);
  return TRUE;
}


bool DropTargets::OnDataText(wxCoord x, wxCoord y, const wxString& data) {
  #ifdef LOGGING
  LOGIT(wxT("DropTargets::OnDataText"));
  #endif
  wxArrayString* pFilenames = m_pcbDndExtn->TextToFilenames(data);
  if(pFilenames->GetCount())
    m_pcbDndExtn->OnDropFiles(1, 1, *pFilenames);
  delete pFilenames;
  return false;
}

wxDragResult DropTargets::OnDragOver(wxCoord x, wxCoord y, wxDragResult def) {
  #ifdef LOGGING
  LOGIT(wxT("DropTargets::OnDragOver"));
  #endif
  return wxDragCopy;
}

wxDragResult DropTargets::OnEnter(wxCoord x, wxCoord y, wxDragResult def) {
  #ifdef LOGGING
  LOGIT(wxT("DropTargets::OnEnter"));
  #endif
  return wxDragCopy;
}

void DropTargets::OnLeave() {
  #ifdef LOGGING
  LOGIT(wxT("DropTargets::OnLeave"));
  #endif
}

void CodeSnippets::OnPrjTreeMouseMotionEvent(wxMouseEvent& event) {
  event.Skip();
  if(not m_IsAttached)
    return;
  m_bMouseCtrlKeyDown = event.ControlDown();
  m_bMouseLeftKeyDown = event.LeftIsDown();
  m_bMouseIsDragging  = event.Dragging();
  wxTreeCtrl* pTree = (wxTreeCtrl*)event.GetEventObject();
  if(m_bMouseIsDragging and m_bMouseLeftKeyDown
      and (not m_bDragCursorOn) and m_prjTreeItemAtKeyDown) {
    m_oldCursor = pTree->GetCursor();
    pTree->SetCursor(*m_pDragCursor);
    m_bDragCursorOn = true;
  } else if(m_bDragCursorOn) {
    pTree->SetCursor(m_oldCursor);
    m_bDragCursorOn = false;
  }
}

void CodeSnippets::OnPrjTreeMouseLeftDownEvent(wxMouseEvent& event) {
  #ifdef LOGGING
  #endif
  event.Skip();
  if(not m_IsAttached)
    return;
  m_bMouseLeftKeyDown = true;
  m_MouseDownX = event.GetX();
  m_MouseDownY = event.GetY();
  #if !wxCHECK_VERSION(2, 8, 12)
  m_prjTreeItemAtKeyDown = 0;
  m_prjTreeItemAtKeyUp = 0;
  #endif
  int hitFlags = 0;
  wxTreeCtrl* pTree = (wxTreeCtrl*)event.GetEventObject();
  wxTreeItemId id = pTree->HitTest(wxPoint(m_MouseDownX, m_MouseDownY), hitFlags);
  if(id.IsOk() and (hitFlags & (wxTREE_HITTEST_ONITEMICON | wxTREE_HITTEST_ONITEMLABEL)))
    m_prjTreeItemAtKeyDown = id;
  #ifdef LOGGING
  #endif
}

void CodeSnippets::OnPrjTreeMouseLeftUpEvent(wxMouseEvent& event) {
  event.Skip();
  if(not m_IsAttached)
    return;
  #ifdef LOGGING
  #endif
  m_bMouseLeftKeyDown = false;
  m_MouseUpX = event.GetX();
  m_MouseUpY = event.GetY();
  #if !wxCHECK_VERSION(2, 8, 12)
  m_prjTreeItemAtKeyUp = 0;
  #endif
  int hitFlags = 0;
  wxTreeCtrl* pTree = (wxTreeCtrl*)event.GetEventObject();
  wxTreeItemId id = pTree->HitTest(wxPoint(m_MouseUpX, m_MouseUpY), hitFlags);
  if(id.IsOk() and (hitFlags & (wxTREE_HITTEST_ONITEMICON | wxTREE_HITTEST_ONITEMLABEL)))
    m_prjTreeItemAtKeyUp = id;
  if(m_bMouseExitedWindow and m_bMouseIsDragging and m_prjTreeItemAtKeyDown) {
  }
  if(m_bMouseExitedWindow) {
    if(pTree->HasCapture()) {
      pTree->ReleaseMouse();
      #if defined(LOGGING)
      LOGIT(_T("Mouse Released"));
      #endif
    } else {
      #if defined(LOGGING)
      LOGIT(_T("Lost Mouse capture"));
      #endif
    }
  }
  m_bMouseExitedWindow = false;
  m_bMouseIsDragging = false;
  if(m_bDragCursorOn) {
    pTree->SetCursor(m_oldCursor);
    m_bDragCursorOn = false;
  }
}

void CodeSnippets::OnPrjTreeMouseLeaveWindowEvent(wxMouseEvent& event) {
  event.Skip();
  m_bBeginInternalDrag = false;
  if(not m_bMouseLeftKeyDown)
    return;
  if(not m_bMouseIsDragging)
    return;
  if(not m_prjTreeItemAtKeyDown)
    return;
  wxTreeCtrl* pTree = (wxTreeCtrl*)event.GetEventObject();
  #ifdef LOGGING
  #endif
  m_bMouseExitedWindow = true;
  #if defined(LOGGING)
  #endif
  if(m_bMouseExitedWindow and m_bMouseIsDragging and m_prjTreeItemAtKeyDown)
    DoPrjTreeExternalDrag(pTree);
  return;
}

void CodeSnippets::DoPrjTreeExternalDrag(wxTreeCtrl* pTree) {
  if(not m_prjTreeItemAtKeyDown)
    return;
  wxTextDataObject* textData = new wxTextDataObject();
  wxFileDataObject* fileData = new wxFileDataObject();
  wxString m_prjTreeText;
  if(not GetTreeSelectionData(pTree, m_prjTreeItemAtKeyDown, m_prjTreeText)) {
    m_prjTreeText = wxEmptyString;
    return;
  }
  static const wxString delim(_T("$%["));
  if(m_prjTreeText.find_first_of(delim) != wxString::npos)
    Manager::Get()->GetMacrosManager()->ReplaceMacros(m_prjTreeText);
  wxDropSource textSource(*textData, pTree);
  textData->SetText(m_prjTreeText);
  wxDropSource fileSource(*fileData, pTree);
  wxString fileName = m_prjTreeText;
  if(not ::wxFileExists(fileName))
    fileName = wxEmptyString;
  if(fileName.IsEmpty()) {
    if(m_prjTreeText.StartsWith(_T("http://")))
      fileName = m_prjTreeText;
    if(m_prjTreeText.StartsWith(_T("file://")))
      fileName = m_prjTreeText;
    fileName = fileName.BeforeFirst('\n');
    fileName = fileName.BeforeFirst('\r');
    if(not fileName.IsEmpty())
      textData->SetText(fileName);
  }
  fileData->AddFile((fileName.Len() > 128) ? wxString(wxEmptyString) : fileName);
  wxDataObjectComposite* data = new wxDataObjectComposite();
  data->Add((wxDataObjectSimple*)textData);
  data->Add((wxDataObjectSimple*)fileData, true);
  wxDropSource source(*data, pTree);
  #ifdef LOGGING
  LOGIT(_T("PrjTree DropSource Text[%s], File[%s]"),
        textData->GetText().c_str(),
        fileData->GetFilenames().Item(0).c_str());
  #endif
  int flags = 0;
  flags |= wxDrag_AllowMove;
  wxDragResult result = source.DoDragDrop(flags);
  #if LOGGING
  wxString pc;
  switch(result) {
    case wxDragError:
      pc = _T("Error!");
      break;
    case wxDragNone:
      pc = _T("Nothing");
      break;
    case wxDragCopy:
      pc = _T("Copied");
      break;
    case wxDragMove:
      pc = _T("Moved");
      break;
    case wxDragLink:
      pc = _T("Linked");
      break;
    case wxDragCancel:
      pc = _T("Cancelled");
      break;
    default:
      pc = _T("Huh?");
      break;
  }
  LOGIT(wxT("DoDragDrop returned[%s]"), pc.GetData());
  #else
  wxUnusedVar(result);
  #endif
  delete textData;
  delete fileData;
  m_TreeText = wxEmptyString;
  #if !wxCHECK_VERSION(2, 8, 12)
  m_prjTreeItemAtKeyDown = 0;
  m_prjTreeItemAtKeyUp = 0;
  #endif
  SendMouseLeftUp(pTree, m_MouseDownX, m_MouseDownY);
}

void CodeSnippets::SendMouseLeftUp(const wxWindow* pWin, const int mouseX, const int mouseY) {
  #if defined(__WXMSW__)
  if(pWin) {
    #ifdef LOGGING
    LOGIT(_T("Sending Mouse LeftKeyUp"));
    #endif
    wxPoint CurrentMousePosn = ::wxGetMousePosition();
    wxPoint fullScreen = pWin->ClientToScreen(wxPoint(mouseX, mouseY));
    MSW_MouseMove(fullScreen.x, fullScreen.y);
    INPUT Input         = {0};
    Input.type          = INPUT_MOUSE;
    Input.mi.dwFlags    = MOUSEEVENTF_LEFTUP;
    ::SendInput(1, &Input, sizeof(INPUT));
    MSW_MouseMove(CurrentMousePosn.x, CurrentMousePosn.y);
  }
  #endif
}

void CodeSnippets::MSW_MouseMove(int x, int y) {
  #if defined(__WXMSW__)
  double fScreenWidth   = ::GetSystemMetrics(SM_CXSCREEN) - 1;
  double fScreenHeight  = ::GetSystemMetrics(SM_CYSCREEN) - 1;
  double fx = x * (65535.0f / fScreenWidth);
  double fy = y * (65535.0f / fScreenHeight);
  INPUT  Input = {0};
  Input.type      = INPUT_MOUSE;
  Input.mi.dwFlags  = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE;
  Input.mi.dx = (LONG)fx;
  Input.mi.dy = (LONG)fy;
  ::SendInput(1, &Input, sizeof(INPUT));
  #endif
}

void CodeSnippets::SetTreeCtrlHandler(wxWindow *p, WXTYPE eventType) {
  if(!p)
    return;
  #ifdef LOGGING
  LOGIT(wxT("CodeSnippets::SetTreeCtrlHandler[%s] %p"), p->GetName().c_str(), p);
  #endif
  p->Connect(wxEVT_LEFT_UP,
             wxMouseEventHandler(CodeSnippets::OnPrjTreeMouseLeftUpEvent),
             NULL, this);
  p->Connect(wxEVT_LEFT_DOWN,
             wxMouseEventHandler(CodeSnippets::OnPrjTreeMouseLeftDownEvent),
             NULL, this);
  p->Connect(wxEVT_MOTION,
             wxMouseEventHandler(CodeSnippets::OnPrjTreeMouseMotionEvent),
             NULL, this);
  p->Connect(wxEVT_LEAVE_WINDOW,
             wxMouseEventHandler(CodeSnippets::OnPrjTreeMouseLeaveWindowEvent),
             NULL, this);
}

void CodeSnippets::RemoveTreeCtrlHandler(wxWindow *p, WXTYPE eventType) {
  if(!p)
    return;
  #ifdef LOGGING
  LOGIT(wxT("CodeSnippets::Detach - detaching to [%s] %p"), p->GetName().c_str(), p);
  #endif
  p->Disconnect(wxEVT_LEFT_UP,
                wxMouseEventHandler(CodeSnippets::OnPrjTreeMouseLeftUpEvent),
                NULL, this);
  p->Disconnect(wxEVT_LEFT_DOWN,
                wxMouseEventHandler(CodeSnippets::OnPrjTreeMouseLeftDownEvent),
                NULL, this);
  p->Disconnect(wxEVT_MOTION,
                wxMouseEventHandler(CodeSnippets::OnPrjTreeMouseMotionEvent),
                NULL, this);
  p->Disconnect(wxEVT_LEAVE_WINDOW,
                wxMouseEventHandler(CodeSnippets::OnPrjTreeMouseLeaveWindowEvent),
                NULL, this);
}

wxString CodeSnippets::FindAppPath(const wxString& argv0, const wxString& cwd, const wxString& appVariableName) {
  wxString str;
  if(!appVariableName.IsEmpty()) {
    str = wxGetenv(appVariableName);
    if(!str.IsEmpty())
      return str;
  }
  #if defined(__WXMAC__) && !defined(__DARWIN__)
  return cwd;
  #endif
  wxString argv0Str = argv0;
  #if defined(__WXMSW__)
  do {
    if(argv0Str.Contains(wxT(".exe")))
      break;
    if(argv0Str.Contains(wxT(".bat")))
      break;
    if(argv0Str.Contains(wxT(".cmd")))
      break;
    argv0Str.Append(wxT(".exe"));
  } while(0);
  #endif
  if(wxIsAbsolutePath(argv0Str)) {
    #if defined(LOGGING)
    LOGIT(_T("FindAppPath: AbsolutePath[%s]"), wxPathOnly(argv0Str).GetData());
    #endif
    return wxPathOnly(argv0Str);
  } else {
    wxString currentDir(cwd);
    if(currentDir.Last() != wxFILE_SEP_PATH)
      currentDir += wxFILE_SEP_PATH;
    str = currentDir + argv0Str;
    if(wxFileExists(str)) {
      #if defined(LOGGING)
      LOGIT(_T("FindAppPath: RelativePath[%s]"), wxPathOnly(str).GetData());
      #endif
      return wxPathOnly(str);
    }
  }
  wxPathList pathList;
  pathList.AddEnvList(wxT("PATH"));
  str = pathList.FindAbsoluteValidPath(argv0Str);
  if(!str.IsEmpty()) {
    #if defined(LOGGING)
    LOGIT(_T("FindAppPath: SearchPath[%s]"), wxPathOnly(str).GetData());
    #endif
    return wxPathOnly(str);
  }
  #if defined(LOGGING)
  LOGIT(_T("FindAppPath: Failed, returning cwd"));
  #endif
  return wxEmptyString;
}

wxWindow* CodeSnippets::FindOpenFilesListWindow() {
  wxFrame* pFrame = Manager::Get()->GetAppFrame();
  int idMenuOpenFilesList = ::wxFindMenuItemId(pFrame, wxT("View"), wxT("Open files list"));
  #if defined(__WXGTK__)
  idMenuOpenFilesList = ::wxFindMenuItemId(pFrame, wxT("View"), wxT("_Open files list"));
  #endif
  int idWindowOpenFilesList = 0;
  if(idMenuOpenFilesList != wxNOT_FOUND) {
    #if defined(LOGGING)
    LOGIT(_T("idMenuOpenFilesList:[%d]"), idMenuOpenFilesList);
    #endif
    idWindowOpenFilesList = idMenuOpenFilesList - 1;
    wxWindow* pOpenFilesListWin = wxWindow::FindWindowById(idWindowOpenFilesList, pFrame);
    if(pOpenFilesListWin) {
      #if defined(LOGGING)
      LOGIT(_T("pOpenFilesListWin[%p] name[%s] label[%s]"), pOpenFilesListWin,
            pOpenFilesListWin->GetName().c_str(),
            pOpenFilesListWin->GetLabel().c_str()
           );
      #endif
      return pOpenFilesListWin;
    }
  }
  return 0;
}
